<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="UTF-8" />
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
  <title>指标矩阵与权重计算</title>
  <link rel="stylesheet" href="https://unpkg.com/element-plus/dist/index.css" />
  <style>
    .assess-page { display: flex; gap: 12px; padding: 12px; }
    .left { width: 280px; }
    .tree { margin-top: 8px; max-height: calc(100vh - 160px); overflow: auto; }
    .right { flex: 1; display: flex; flex-direction: column; gap: 12px; }
    .matrix-table { overflow: auto; }
    table.m { border-collapse: collapse; width: 100%; }
    table.m th, table.m td { border: 1px solid #e5e5e5; padding: 6px; text-align: center; }
    .meta { margin-top: 8px; display: flex; gap: 12px; align-items: center; }
    .toolbar { padding: 6px 0; }
  </style>
</head>
<body>
  <div id="app">
    <div class="assess-page">
      <div class="left">
        <el-input v-model="treeFilter" placeholder="搜索指标" size="small" clearable />
        <el-tree
          class="tree"
          :data="treeData"
          :props="treeProps"
          node-key="id"
          highlight-current
          :filter-node-method="filterNode"
          @node-click="onNodeClick"
        />
      </div>
      <div class="right">
        <div class="toolbar">
          <el-form :inline="true" size="small">
            <el-form-item label="RootId">
              <el-input-number v-model="rootId" :min="1" />
            </el-form-item>
            <el-form-item>
              <el-button type="primary" @click="loadTree">加载树</el-button>
            </el-form-item>
            <el-form-item label="父节点">
              <el-input v-model="parentTitle" placeholder="请选择左侧父节点" disabled />
            </el-form-item>
            <el-form-item label="版本">
              <el-input-number v-model="version" :min="1" />
            </el-form-item>
            <el-form-item label="方案ID">
              <el-input-number v-model="schemeId" :min="1" placeholder="可选" />
            </el-form-item>
            <el-form-item>
              <el-button :disabled="!parentId" @click="loadChildrenAndMatrix">加载子项与矩阵</el-button>
            </el-form-item>
            <el-form-item>
              <el-button type="primary" :disabled="!parentId" @click="saveMatrix">保存矩阵</el-button>
              <el-button type="success" :disabled="!parentId" @click="doCompute">计算权重</el-button>
              <el-button :disabled="!parentId" @click="resetMatrix">重置矩阵</el-button>
            </el-form-item>
          </el-form>
        </div>

        <el-card v-if="children.length > 0" class="matrix-card" shadow="hover">
          <template #header>成对比较矩阵</template>
          <div class="matrix-table">
            <div style="margin-bottom:8px;display:flex;gap:8px;align-items:center;">
              <span>快捷设置（先点击上三角单元格获取焦点）：</span>
              <el-button size="small" @click="quickSet(1/9)">1/9</el-button>
              <el-button size="small" @click="quickSet(1/7)">1/7</el-button>
              <el-button size="small" @click="quickSet(1/5)">1/5</el-button>
              <el-button size="small" @click="quickSet(1/3)">1/3</el-button>
              <el-button size="small" @click="quickSet(1)">1</el-button>
              <el-button size="small" @click="quickSet(3)">3</el-button>
              <el-button size="small" @click="quickSet(5)">5</el-button>
              <el-button size="small" @click="quickSet(7)">7</el-button>
              <el-button size="small" @click="quickSet(9)">9</el-button>
            </div>
            <table class="m">
              <thead>
                <tr>
                  <th></th>
                  <th v-for="(c, j) in children" :key="'h'+j">{{ c.name }}</th>
                </tr>
              </thead>
              <tbody>
                <tr v-for="(row, i) in matrix" :key="'r'+i">
                  <th>{{ children[i].name }}</th>
                  <td v-for="(val, j) in row" :key="'c'+i+'-'+j">
                    <template v-if="i === j">
                      <el-input-number v-model="matrix[i][j]" :min="1" :max="1" :step="0.1" disabled />
                    </template>
                    <template v-else-if="i < j">
                      <el-input-number v-model="matrix[i][j]" :min="0.000001" :step="0.1" @focus="onCellFocus(i,j)" @change="onUpperChange(i,j)" />
                    </template>
                    <template v-else>
                      <el-input-number v-model="matrix[i][j]" :step="0.1" disabled />
                    </template>
                  </td>
                </tr>
              </tbody>
            </table>
          </div>
        </el-card>

        <el-card v-if="weights.length>0" class="weight-card" shadow="hover">
          <template #header>计算结果</template>
          <el-table :data="weights" size="small" border>
            <el-table-column prop="name" label="子指标" />
            <el-table-column prop="weight" label="权重">
              <template #default="{ row }">{{ row.weight.toFixed(6) }}</template>
            </el-table-column>
          </el-table>
          <div class="meta">
            <span>λmax: {{ meta.lambdaMax ?? '-' }}</span>
            <span>CI: {{ meta.ci ?? '-' }}</span>
            <span>CR: {{ meta.cr ?? '-' }}</span>
            <el-tag v-if="meta.cr!=null && meta.cr>0.1" type="warning" size="small">CR > 0.1，建议调整判断</el-tag>
          </div>
        </el-card>

        <el-empty v-if="parentId && children.length===0" description="该父节点无子节点" />
        <el-empty v-if="!parentId" description="请选择左侧父节点" />
      </div>
    </div>
  </div>

  <script src="https://unpkg.com/vue@3"></script>
  <script src="https://unpkg.com/element-plus"></script>
  <script src="https://unpkg.com/axios/dist/axios.min.js"></script>
  <script>
    const { createApp, ref, reactive } = Vue;
    const app = createApp({
      setup() {
        const http = axios.create({ baseURL: '/api' });
        http.interceptors.response.use((res) => {
          const data = res.data;
          if (data?.code && data.code !== 200 && data?.rows === undefined) {
            return Promise.reject(new Error(data?.msg || '请求失败'));
          }
          return data;
        });

        const treeProps = { label: 'name', children: 'children' };
        const treeData = ref([]);
        const treeFilter = ref('');
        const rootId = ref(1);

        const parentId = ref(null);
        const parentTitle = ref('');
        const version = ref(1);
        const schemeId = ref();

        const children = ref([]);
        const matrix = ref([]);
        const weights = ref([]);
        const meta = reactive({ lambdaMax: undefined, ci: undefined, cr: undefined });
        const focused = reactive({ i: null, j: null });

        const filterNode = (value, data) => {
          if (!value) return true;
          return (data.name || '').toLowerCase().includes(value.toLowerCase());
        };

        const loadTree = async () => {
          try {
            const res = await http.get('/assess/indicator/tree', { params: { rootId: rootId.value } });
            treeData.value = res;
            ElementPlus.ElMessage.success('加载成功');
          } catch (e) {
            ElementPlus.ElMessage.error(e.message || '加载树失败');
          }
        };

        const onNodeClick = (node) => {
          parentId.value = node.id;
          parentTitle.value = node.name;
          clearResult();
          loadChildrenAndMatrix();
        };

        const loadChildren = async () => {
          const findNode = (nodes, id) => {
            for (const n of nodes || []) {
              if (n.id === id) return n;
              const r = findNode(n.children, id);
              if (r) return r;
            }
            return null;
          };
          const node = findNode(treeData.value, parentId.value);
          const arr = [];
          (node?.children || []).forEach((c) => arr.push({ id: c.id, name: c.name }));
          children.value = arr;
        };

        const loadMatrix = async () => {
          const n = children.value.length;
          buildMatrix();
          if (n === 0) return;
          const resp = await http.get('/assess/matrix/page', { params: { pageNum: 1, pageSize: n*n+5, parentId: parentId.value, version: version.value, schemeId: schemeId.value } });
          const rows = resp.rows || [];
          rows.forEach(e => {
            const i = e.iIndex, j = e.jIndex, v = e.value;
            if (i === j) matrix.value[i][j] = 1;
            else { matrix.value[i][j] = v; matrix.value[j][i] = 1 / v; }
          });
        };

        const loadChildrenAndMatrix = async () => {
          if (!parentId.value) return;
          await loadChildren();
          await loadMatrix();
        };

        const buildMatrix = () => {
          const n = children.value.length;
          const m = [];
          for (let i = 0; i < n; i++) {
            m[i] = [];
            for (let j = 0; j < n; j++) {
              m[i][j] = i === j ? 1 : (j < i ? 1 : 1);
            }
          }
          matrix.value = m;
        };

        const onCellFocus = (i, j) => { focused.i = i; focused.j = j; };

        const onUpperChange = (i, j) => {
          const v = matrix.value[i][j];
          if (!v || v <= 0) matrix.value[i][j] = 1;
          matrix.value[j][i] = 1 / matrix.value[i][j];
        };

        const quickSet = (val) => {
          if (focused.i == null || focused.j == null) return;
          const i = focused.i, j = focused.j;
          if (i >= j) return;
          matrix.value[i][j] = val;
          onUpperChange(i, j);
        };

        const clearResult = () => {
          weights.value = [];
          meta.lambdaMax = undefined;
          meta.ci = undefined;
          meta.cr = undefined;
        };

        const resetMatrix = () => { buildMatrix(); clearResult(); };

        const saveMatrix = async () => {
          if (!parentId.value) return;
          const n = children.value.length;
          if (n === 0) { ElementPlus.ElMessage.warning('该父节点无子节点'); return; }
          const elements = [];
          for (let i = 0; i < n; i++) {
            for (let j = i; j < n; j++) {
              elements.push({ iIndex: i, jIndex: j, value: matrix.value[i][j] });
            }
          }
          try {
            await http.post('/assess/matrix/batch', elements, { params: { parentId: parentId.value, version: version.value, schemeId: schemeId.value } });
            ElementPlus.ElMessage.success('保存成功');
          } catch (e) { ElementPlus.ElMessage.error(e.message || '保存失败'); }
        };

        const doCompute = async () => {
          if (!parentId.value) return;
          try {
            const res = await http.post('/assess/weight/compute', null, { params: { parentId: parentId.value, version: version.value, schemeId: schemeId.value } });
            const rows = res.data || res;
            const map = new Map(children.value.map((c) => [c.id, c.name]));
            weights.value = (rows || []).map((r) => ({ name: map.get(r.childId) || r.childId, weight: r.weight }));
            if ((rows || []).length > 0) { meta.lambdaMax = rows[0].lambdaMax; meta.ci = rows[0].ci; meta.cr = rows[0].cr; }
            ElementPlus.ElMessage.success('计算完成');
          } catch (e) { ElementPlus.ElMessage.error(e.message || '计算失败'); }
        };

        return { treeProps, treeData, treeFilter, rootId, parentId, parentTitle, version, schemeId, children, matrix, weights, meta, filterNode, loadTree, onNodeClick, loadChildrenAndMatrix, onCellFocus, onUpperChange, quickSet, resetMatrix, saveMatrix, doCompute };
      },
    });
    app.use(ElementPlus);
    app.mount('#app');
  </script>
</body>
</html>


